/******************************************************
*  Copyright(c) pages.ilovezxl.top
*  FileName : uart_linux.c
*  Author : rivulet.dave
*  Description : uart Linux平台实现
*  Data : 2020-08-01
******************************************************/

/*********************************************************************
 * INCLUDES
 */

#include "stdrope.h"

#include "hallib.h"
#include "halagent.h"

#if (HAL_UART == _ENABLE && HAL_PRODUCER == _HAL_LINUX_)

#include <unistd.h>
#include <sys/types.h>  
#include <sys/stat.h>   
#include <fcntl.h>      /*文件控制定义*/
#include <termios.h>    /*PPSIX 终端控制定义*/
#include <errno.h>
#include "log.h"
#include "uart_linux.h"

/*********************************************************************
 *  EXTERNAL VARIABLES
 */

/*********************************************************************
 * MACROS
 */

#define MOD_NAME                TOSTR(HW_UART)

/*********************************************************************
 * TYPES
 */

typedef struct _uart_linux_cb
{
    HAL_OPER_S stBaseObj;  //须在第一个位置
    const char *pName;
    int32_t iFd;
} UART_LINUX_CB_S;

/*********************************************************************
 * FUNCTIONS DECLARE
 */

static hd_t UART_OpenByLinux(uint32_t uiIdx);
static int32_t UART_CloseByLinux(hd_t hModuleDescriptor);
static int32_t UART_ReadByLinux(hd_t hModuleDescriptor, uint8_t *pUserBuf, uint32_t uiSize, uint32_t uiFlag);
static int32_t UART_WriteByLinux(hd_t hModuleDescriptor, const uint8_t *pUserData, uint32_t uiSize, uint32_t uiFlag);
static int32_t UART_CtrlByLinux(hd_t hModuleDescriptor, uint32_t uiCmdKey, void *pValue);
static int32_t UART_LockByLinux(hd_t hModuleDescriptor, uint32_t uiFlag);
static int32_t UART_UnlockByLinux(hd_t hModuleDescriptor, uint32_t uiFlag);

static int32_t UART_SetBaudrateByLinux(int32_t fd, int32_t baudrate);
static int32_t UART_SetParityByLinux(int32_t fd, int32_t databits, int32_t stopbits, int32_t parity);

/*********************************************************************
 * VARIABLES
 */

static HAL_OPER_S s_stUartBaseLinuxObj;
static UART_LINUX_CB_S s_astUartLinuxCb[HAL_UART_NUM];
static char *s_acUartNameList[] = HAL_UART_NAMES;

/*********************************************************************
 * FUNCTIONS
 */

/**
 *   @Description: UART初始化相关接口，指针
 *   @param : NA
 *   @return ：_OK/_ERR
 */
HAL_OPER_S *UART_ObjNewByLinux(void)
{
    s_stUartBaseLinuxObj.pThis = &s_stUartBaseLinuxObj;
    s_stUartBaseLinuxObj.pfOpen = UART_OpenByLinux;
    s_stUartBaseLinuxObj.pfClose = UART_CloseByLinux;
    s_stUartBaseLinuxObj.pfLock = UART_LockByLinux;
    s_stUartBaseLinuxObj.pfUnlock = UART_UnlockByLinux;
    s_stUartBaseLinuxObj.pfRead = UART_ReadByLinux;
    s_stUartBaseLinuxObj.pfWrite = UART_WriteByLinux;
    s_stUartBaseLinuxObj.pfReadOnce = NULL;
    s_stUartBaseLinuxObj.pfWriteOnce = NULL;
    s_stUartBaseLinuxObj.pfCtrl = UART_CtrlByLinux;
    
    for (uint32_t i = 0; i < HAL_UART_NUM; i++)
    {
        s_astUartLinuxCb[i].pName = s_acUartNameList[i];
    }
    return &s_stUartBaseLinuxObj;
}

/**
 *   @Description: UART去初始化、退出
 *   @param : NA
 *   @return ：_OK/_ERR
 */
int32_t UART_ObjDeleteByLinux(void)
{
    return _OK;
}

static hd_t UART_OpenByLinux(uint32_t uiIdx)
{
    int32_t iFd;

    if (HAL_UART_NUM <= uiIdx)
    {
        return NULL;
    }

    iFd = open(s_astUartLinuxCb[uiIdx].pName, O_RDWR | O_NOCTTY);
    if (0 > iFd)
    {
        LOG_ERR(MOD_NAME, "uart:%s open fail, %s\n", s_astUartLinuxCb[uiIdx].pName, strerror(errno));
        return NULL;
    }

    LOG_DBG(MOD_NAME, "uart:%s open succ, idx:%d, fd:%d.\n", s_astUartLinuxCb[uiIdx].pName, uiIdx, iFd);
    
    s_astUartLinuxCb[uiIdx].iFd = iFd;
    s_astUartLinuxCb[uiIdx].stBaseObj = s_stUartBaseLinuxObj;
    s_astUartLinuxCb[uiIdx].stBaseObj.pThis = &s_astUartLinuxCb[uiIdx];
    return (hd_t)&s_astUartLinuxCb[uiIdx];
}

static int32_t UART_CloseByLinux(hd_t hModuleDescriptor)
{
    UART_LINUX_CB_S *pstUartObj = (UART_LINUX_CB_S *)hModuleDescriptor;

    if (!pstUartObj)
    {
        return _ERR;
    }

    if (0 < pstUartObj->iFd)
    {
        close(pstUartObj->iFd);
        pstUartObj->iFd = -1;
    }

    LOG_DBG(MOD_NAME, "uart:%s close.\n", pstUartObj->pName);
    return _OK;
}

static int32_t UART_ReadByLinux(hd_t hModuleDescriptor, uint8_t *pUserBuf, uint32_t uiSize, uint32_t uiFlag)
{
    int32_t iRet;
    int32_t flags;
    UART_LINUX_CB_S *pstUartObj = (UART_LINUX_CB_S *)hModuleDescriptor;

    if (!pstUartObj)
    {
        return _ERR;
    }

    if (0 > pstUartObj->iFd)
    {
        return _ERR;
    }

    flags = fcntl(pstUartObj->iFd, F_GETFL, 0);
    if (flags < 0)
    {
        LOG_ERR(MOD_NAME, "uart fcntl get fail.\n");
        return _ERR;
    }

    if (_GET_BIT(uiFlag, H_FLAG_BLOCK))
    {
        _CLEAR_BIT(flags, O_NONBLOCK);
    }
    else
    {
        _SET_BIT(flags, O_NONBLOCK);
    }

    if (fcntl(pstUartObj->iFd, F_SETFL, flags) < 0)
    {
        LOG_ERR(MOD_NAME, "uart fcntl set fail.\n");
        return _ERR;
    }

    iRet = read(pstUartObj->iFd, pUserBuf, uiSize);
    if (0 > iRet)
    {
        LOG_ERR(MOD_NAME, "uart:%s write fail:%s\n", pstUartObj->pName, strerror(errno));
        return _ERR;
    }
    return iRet;
}

static int32_t UART_WriteByLinux(hd_t hModuleDescriptor, const uint8_t *pUserData, uint32_t uiSize, uint32_t uiFlag)
{
    int32_t iRet;
    UART_LINUX_CB_S *pstUartObj = (UART_LINUX_CB_S *)hModuleDescriptor;

    if (!pstUartObj)
    {
        return _ERR;
    }

    if (0 > pstUartObj->iFd)
    {
        return _ERR;
    }

    iRet = write(pstUartObj->iFd, pUserData, uiSize);
    if (0 > iRet)
    {
        LOG_ERR(MOD_NAME, "uart:%s read fail:%s\n", pstUartObj->pName, strerror(errno));
        return _ERR;
    }
    return iRet;
}

static int32_t UART_CtrlByLinux(hd_t hModuleDescriptor, uint32_t uiCmdKey, void *pValue)
{
    UART_INIT_CB_S *pstUartOpt = (UART_INIT_CB_S *)pValue;
    UART_LINUX_CB_S *pstUartObj = (UART_LINUX_CB_S *)hModuleDescriptor;

    if (!pstUartObj || !pstUartOpt)
    {
        return _ERR;
    }

    if (0 > pstUartObj->iFd)
    {
        LOG_ERR(MOD_NAME, "uart:%s fd:%d error.\n", pstUartObj->pName, pstUartObj->iFd);
        return _ERR;
    }

    if (CTRL_UART_INIT == uiCmdKey)
    {
        if (_OK != UART_SetBaudrateByLinux(pstUartObj->iFd, pstUartOpt->uiBaudrate))
        {
            return _ERR;
        }

        if (_OK != UART_SetParityByLinux(pstUartObj->iFd, pstUartOpt->ucDataBits, pstUartOpt->ucStopBits, pstUartOpt->ucParity))
        {
            return _ERR;
        }

        LOG_DBG(MOD_NAME, "uart:%s init ok, %d, %d, %d, %d\n", 
                pstUartObj->pName, pstUartOpt->uiBaudrate, pstUartOpt->ucDataBits, pstUartOpt->ucStopBits, pstUartOpt->ucParity);
    }
    return _OK;
}

static int32_t UART_LockByLinux(hd_t hModuleDescriptor, uint32_t uiFlag)
{
    return _OK;
}

static int32_t UART_UnlockByLinux(hd_t hModuleDescriptor, uint32_t uiFlag)
{
    return _OK;
}

/**
 *@brief  设置串口通信速率
 *@param  fd         类型 int  打开串口的文件句柄
 *@param  baudrate   类型 int  串口速度
 *@return int32_t
*/
static int32_t UART_SetBaudrateByLinux(int32_t fd, int32_t baudrate)
{
    int32_t iStatus;
    struct termios stOpt;
    const int32_t auiLinuxBaudrate[] = { B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300 };
    const int32_t auiBaudrate[] = { 115200, 38400,  19200,  9600,  4800,  2400,  1200,  300 };
    
    tcgetattr(fd, &stOpt); 
    for (uint32_t i= 0;  i < _ARRAY_SIZE(auiLinuxBaudrate);  i++)
    { 
        if (baudrate == auiBaudrate[i])
        {     
            tcflush(fd, TCIOFLUSH);     
            cfsetispeed(&stOpt, auiLinuxBaudrate[i]);  
            cfsetospeed(&stOpt, auiLinuxBaudrate[i]);   
            iStatus = tcsetattr(fd, TCSANOW, &stOpt);
            if (iStatus != 0)
            {        
                LOG_ERR(MOD_NAME, "uart set baudrate fail:%s.\n", strerror(errno));  
                return _ERR;
            }
            tcflush(fd, TCIOFLUSH);
            return _OK;
        }
    }

    LOG_ERR(MOD_NAME, "uart baudrate:%d is wrong.\n", baudrate);
    return _ERR;
}

/**
 *@brief  设置串口数据位，停止位和效验位
 *@param  fd     类型  int  打开的串口文件句柄
 *@param  databits 类型  int 数据位   取值 为 7 或者8
 *@param  stopbits 类型  int 停止位   取值为 1 或者2
 *@param  parity  类型  int  效验类型
*/
static int32_t UART_SetParityByLinux(int32_t fd, int32_t databits, int32_t stopbits, int32_t parity)
{
    struct termios options;
    if (tcgetattr( fd,&options) !=  0)
    {
        LOG_ERR(MOD_NAME, "uart set parity fail.\n");
        return _ERR;
    }

    options.c_cflag &= ~CSIZE;
    switch (databits) /*设置数据位数*/
    {
        case 7:
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag |= CS8;
            break;
        default:
            LOG_ERR(MOD_NAME, "unsupported data size.\n");
            return _ERR;
    }

    switch (parity)
    {
        case PARITY_NONE:
            options.c_cflag &= ~PARENB; /* Clear parity enable */
            options.c_iflag &= ~INPCK;  /* Enable parity checking */
            break;
        case PARITY_ODD:
            options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
            options.c_iflag |= INPCK;             /* Disnable parity checking */
            break;
        case PARITY_EVEN:
            options.c_cflag |= PARENB;  /* Enable parity */
            options.c_cflag &= ~PARODD; /* 转换为偶效验*/
            options.c_iflag |= INPCK;   /* Disnable parity checking */
            break;
        case PARITY_SPACE:  /*as no parity*/
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            break;
        default:
            LOG_ERR(MOD_NAME, "unsupported parity.\n");
            return _ERR;
    }

    /* 设置停止位*/
    switch (stopbits)
    {
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            LOG_ERR(MOD_NAME, "unsupported stop bits.\n");
            return _ERR;
    }

    /* Set input parity option */
    if (PARITY_NONE != parity)
    {
        options.c_iflag |= INPCK;
    }
    tcflush(fd,TCIFLUSH);
    options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/
    options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
    if (tcsetattr(fd,TCSANOW,&options) != 0)
    {
        LOG_ERR(MOD_NAME, "uart set attr fail.\n");
        return _ERR;
    }
    return _OK;
}

#endif  // HAL_UART == _ENABLE && HAL_PRODUCER == _HAL_LINUX_

/************************************** The End Of File **************************************/

